﻿using System;
using System.Collections.Generic;
using System.Text;

namespace TileGert
{
  /// <summary>
  /// GertCircle is a simple circle primitive. There are methods for various
  /// math like collision detection and such thing. When the circle is drawn
  /// it is adaptivly tesselated to a polyline.
  /// </summary>
  public class GertCircle
  {
    private GertPoint _center = null;
    private float _radius = 1.0f;
    private float _pi = (float)Math.PI;
    private GertTransformedColoredVertex[] _vertices = null;

    /// <summary>
    /// Creates a new GertCircle centered at (0,0) with a radius of 1.
    /// </summary>
    public GertCircle()
    {
      _center = new GertPoint();
    }

    /// <summary>
    /// Creates a new circle centered at the given center with the given radius.
    /// </summary>
    /// <param name="center">The center of the circle.</param>
    /// <param name="radius">The radius of the circle.</param>
    public GertCircle(GertPoint center, float radius)
    {
      _center = center;
      _radius = radius;
    }

    /// <summary>
    /// Creates a new circle centered at the given coordinates with the given radius.
    /// </summary>
    /// <param name="x">The x coordinate of the center.</param>
    /// <param name="y">The y coordinate of the center.</param>
    /// <param name="radius">The raius of the circle.</param>
    public GertCircle(float x, float y, float radius)
    {
      _center = new GertPoint(x, y);
      _radius = radius;
    }

    /// <summary>
    /// Checks if the given point is inside of the circle.
    /// </summary>
    /// <param name="x">The x component of the point.</param>
    /// <param name="y">The y component of the point.</param>
    /// <returns>True if the point lies inside of or on the border of the circle, else False.</returns>
    public bool PointIsInside(float x, float y)
    {
      return PointIsInside(new GertPoint(x, y));
    }

    /// <summary>
    /// Checks if the given point is inside of the circle.
    /// </summary>
    /// <param name="point">The point to be checked.</param>
    /// <returns>True if the point lies inside of or on the border of the circle, else False.</returns>
    /// <returns></returns>
    public bool PointIsInside(GertPoint point)
    {
      return _center.DistanceTo(point) <= _radius;
    }

    /// <summary>
    /// Calculates the closest point from the given point on the "surface" of the circle.
    /// </summary>
    /// <param name="point">A point.</param>
    /// <returns>The closest point on the surface of the circle to the given point.</returns>
    public GertPoint ClosestPoint(GertPoint point)
    {
      GertVector vec = new GertVector(point.X - _center.X, point.Y - _center.Y);
      vec.Normalize();
      return GertPoint.Transform(_center, vec, _radius);
    }

    /// <summary>
    /// Calculates the circumference of the circle.
    /// </summary>
    /// <returns>The circumference of the circle.</returns>
    public float Circumference()
    {
      return 2 * _pi * _radius;
    }

    /// <summary>
    /// Gets the vertices that make up the circle. This method is used for drawing the circle.
    /// The number of vertices is calculated adaptively so the circle always looks round but never
    /// has too much vertices.<br/>
    /// The vertices are created lazyly the first time this method is called. After that the
    /// vertices are cached and there is no calculation involved anymore.
    /// </summary>
    /// <param name="color">A color that is assigned to the vertices.</param>
    /// <returns>An array of pretransformed vertices that make up the circle.</returns>
    public GertTransformedColoredVertex[] GetVertices(GertColor color)
    {
      if (_vertices == null)
      {
        int numVerts = (int)((20 + _radius * 0.5) / 2);
        numVerts = GertMisc.Max(4, numVerts);
        System.Diagnostics.Debug.WriteLine("Vertices: " + numVerts);

        _vertices = new GertTransformedColoredVertex[numVerts + 1];
        float temp = (float)(Math.PI * 2.0) / numVerts;
        int intColor = color.ToInt();
        for (int i = 0; i < _vertices.Length; i++)
        {
          _vertices[i] = new GertTransformedColoredVertex();
          _vertices[i].Position.X = (float)Math.Sin(i * temp) * _radius + _center.X;
          _vertices[i].Position.Y = (float)Math.Cos(i * temp) * _radius + _center.Y;
          _vertices[i].Color = intColor;
        }
      }
      return _vertices;
    }

  }
}
